package com.corticon.client.validation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;


/**
 * Convenience class for resolving/formatting Spring Validation
 * <code>Errors</code> into <code>String</code> messages using an underlying
 * <code>MessageSource</code>. Due to usage of a <code>MessageSource</code>
 * bundled, localized, and internationalized messages are supported.
 * 
 * @author Heweiya
 */
public class ValidationMessageFormatter {
	private String fieldErrorMsgKeySuffix = ".errors";
	private MessageSource messageSource;

	/**
	 * Get the first <code>FieldError</code> validation message for the given
	 * fieldName.
	 * 
	 * @param errors
	 *            The validation <code>Errors<code>.
	 * @param fieldName The fieldName to retrieve the error message for.
	 * @return The <code>String</code> validation message or null.
	 */
	public String getFieldErrorMessage(Errors errors, String fieldName) {
		Locale locale = LocaleContextHolder.getLocale();
		return getFieldErrorMessage(errors, fieldName, null, locale);
	}

	/**
	 * Get the first <code>FieldError</code> validation message for the given
	 * fieldName.
	 * 
	 * @param errors
	 *            The validation <code>Errors<code>.
	 * @param fieldName The fieldName to retrieve the error message for.
	 * @param locale The desired <code>Locale</code> of the message.
	 * @return The <code>String</code> validation message or null.
	 */
	public String getFieldErrorMessage(Errors errors, String fieldName, Locale locale) {
		return getFieldErrorMessage(errors, fieldName, null, locale);
	}

	/**
	 * Get the first <code>FieldError</code> validation message for the given
	 * fieldName.
	 * 
	 * @param errors
	 *            The validation <code>Errors<code>.
	 * @param fieldName The fieldName to retrieve the error message for.
	 * @param args Arguments to be populated within the validation message.
	 * @return The <code>String</code> validation message or null.
	 */
	public String getFieldErrorMessage(Errors errors, String fieldName, Object[] args) {
		Locale locale = LocaleContextHolder.getLocale();
		return getFieldErrorMessage(errors, fieldName, args, locale);
	}

	/**
	 * Get the first <code>FieldError</code> validation message for the given
	 * fieldName.
	 * 
	 * @param errors
	 *            The validation <code>Errors<code>.
	 * @param fieldName The fieldName to retrieve the error message for.
	 * @param args Arguments to be populated within the validation message.
	 * @param locale The desired <code>Locale</code> of the message.
	 * @return The <code>String</code> validation message or null.
	 */
	public String getFieldErrorMessage(Errors errors, String fieldName, Object[] args, Locale locale) {
		String message = null;

		FieldError fieldError = errors.getFieldError(fieldName);
		if (fieldError != null) {
			String codes[] = fieldError.getCodes();
			for (String code : codes) {
				message = messageSource.getMessage(code, args, null, locale);

				if (message != null) {
					// A message matching the code has been found
					break;
				}
			}
			if (message == null) {
				message = fieldError.getDefaultMessage();
			}
		}

		return message;
	}

	/**
	 * This method converts the error messages from
	 * <code>org.springframework.validation.Errors</code> to java.util.Map.
	 * 
	 * @param validateObject
	 * @param errors
	 * @param locale
	 * @return
	 */
	public Map<String, List<String>> convertErrorsMessageToMap(Object validateObject, Errors errors, Locale locale) {
		Map<String, List<String>> errorMessages = new HashMap<String, List<String>>();

		// Get all fields of the validate object.
		Field[] fields = validateObject.getClass().getDeclaredFields();
		Field[] superFields = validateObject.getClass().getSuperclass().getDeclaredFields();
		Field[] allFields = (Field[]) ArrayUtils.addAll(fields, superFields);

		for (Field field : allFields) {
			List<String> fieldErrorMessages = new ArrayList<String>();

			// Get all field errors according to the field name.
			List fieldErrorList = errors.getFieldErrors(field.getName());
			for (int i = 0; i < fieldErrorList.size(); i++) {

				FieldError fieldError = (FieldError) fieldErrorList.get(i);
				String fieldErrorMessage = "";
				// for (String errorCode : fieldError.getCodes()) {
				fieldErrorMessage = messageSource.getMessage(fieldError.getCode(), fieldError.getArguments(), fieldError.getDefaultMessage(), locale);
				// if (!fieldErrorMessage.equals("default message")) {
				// break;
				// }
				fieldErrorMessages.add(fieldErrorMessage);
			}
			errorMessages.put(field.getName() + fieldErrorMsgKeySuffix, fieldErrorMessages);
		}

		return errorMessages;
	}

	public void setFieldErrorMsgKeySuffix(String fieldErrorMsgKeySuffix) {
		this.fieldErrorMsgKeySuffix = fieldErrorMsgKeySuffix;
	}

	/**
	 * Set the <code>MessageSource</code>.
	 * 
	 * @param messageSource
	 *            The <code>MessageSource</code>.
	 */
	public void setMessageSource(MessageSource messageSource) {
		this.messageSource = messageSource;
	}
}